package com.netsdk.demo.customize;

import static com.netsdk.lib.Utils.getOsPrefix;

import java.io.File;

import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;

import com.netsdk.demo.util.CaseMenu;
import com.netsdk.lib.NetSDKLib;
import com.netsdk.lib.ToolKits;
import com.netsdk.lib.NetSDKLib.LLong;
import com.netsdk.lib.callback.impl.DefaultDisconnectCallback;
import com.netsdk.lib.callback.impl.DefaultHaveReconnectCallBack;
import com.netsdk.lib.enumeration.EM_DELETE_BY_SOURCEUID_ERRCODE;
import com.netsdk.lib.enumeration.EM_WORKSUIT_APPEND_ERRCODE;
import com.netsdk.lib.enumeration.EM_WORKSUIT_GROUP_POLICY;
import com.netsdk.lib.enumeration.EM_WORKSUIT_GROUP_TYPE;
import com.netsdk.lib.structure.ALARM_WORKSUIT_FEATURE_ABSTRACT_INFO;
import com.netsdk.lib.structure.NET_ALARM_REID_CLUSTER_STATE_INFO;
import com.netsdk.lib.structure.NET_CB_VIDEOSTAT_STREAM;
import com.netsdk.lib.structure.NET_IN_ATTACH_VIDEOSTAT_STREAM;
import com.netsdk.lib.structure.NET_IN_CREATE_WORKSUIT_COMPARE_GROUP;
import com.netsdk.lib.structure.NET_IN_DELETE_WORKSUIT_BY_SOURCEUID;
import com.netsdk.lib.structure.NET_IN_DELETE_WORKSUIT_COMPARE_GROUP;
import com.netsdk.lib.structure.NET_IN_DO_FIND_DETAIL_CLUSTER;
import com.netsdk.lib.structure.NET_IN_FIND_WORKSUIT_COMPARE_GROUP;
import com.netsdk.lib.structure.NET_IN_GET_CLUSTER_STATE_INFO;
import com.netsdk.lib.structure.NET_IN_MULTI_APPEND_TO_WORKSUIT_GROUP;
import com.netsdk.lib.structure.NET_IN_START_FIND_DETAIL_CLUSTER;
import com.netsdk.lib.structure.NET_IN_STOP_FIND_DETAIL_CLUSTER_INFO;
import com.netsdk.lib.structure.NET_OUT_ATTACH_VIDEOSTAT_STREAM;
import com.netsdk.lib.structure.NET_OUT_CREATE_WORKSUIT_COMPARE_GROUP;
import com.netsdk.lib.structure.NET_OUT_DELETE_WORKSUIT_BY_SOURCEUID;
import com.netsdk.lib.structure.NET_OUT_DELETE_WORKSUIT_COMPARE_GROUP;
import com.netsdk.lib.structure.NET_OUT_DO_FIND_DETAIL_CLUSTER;
import com.netsdk.lib.structure.NET_OUT_FIND_WORKSUIT_COMPARE_GROUP;
import com.netsdk.lib.structure.NET_OUT_GET_CLUSTER_STATE_INFO;
import com.netsdk.lib.structure.NET_OUT_MULTI_APPEND_TO_WORKSUIT_GROUP;
import com.netsdk.lib.structure.NET_OUT_START_FIND_DETAIL_CLUSTER;
import com.netsdk.lib.structure.NET_OUT_STOP_FIND_DETAIL_CLUSTER_INFO;
import com.netsdk.lib.structure.NET_WORKSUIT_ABSTRACT_INFO;
import com.netsdk.lib.structure.NET_WORKSUIT_COMPARE_GROUP_INFO;
import com.netsdk.lib.structure.NET_WORKSUIT_SAMPLE_INFO;
import com.sun.jna.Memory;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;

public class REIDConfigDemo {
	// SDk对象初始化
	public static final NetSDKLib netsdk = NetSDKLib.NETSDK_INSTANCE;
	public static final NetSDKLib configsdk = NetSDKLib.CONFIG_INSTANCE;

	// 判断是否初始化
	private static boolean bInit = false;
	// 判断log是否打开
	private static boolean bLogOpen = false;
	// 设备信息
	private NetSDKLib.NET_DEVICEINFO_Ex deviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex();
	// 登录句柄
	private NetSDKLib.LLong m_hLoginHandle = new NetSDKLib.LLong(0);

	// 订阅统计通道数据时句柄
	private NetSDKLib.LLong attachHandle = new NetSDKLib.LLong(0);

	// 回调函数需要是静态的，防止被系统回收
	// 断线回调
	private static NetSDKLib.fDisConnect disConnectCB = DefaultDisconnectCallback.getINSTANCE();
	// 重连回调
	private static NetSDKLib.fHaveReConnect haveReConnectCB = DefaultHaveReconnectCallBack.getINSTANCE();

	// 编码格式
	public static String encode;

	static {
		String osPrefix = getOsPrefix();
		if (osPrefix.toLowerCase().startsWith("win32-amd64")) {
			encode = "GBK";
		} else if (osPrefix.toLowerCase().startsWith("linux-amd64")) {
			encode = "UTF-8";
		}
	}

	/**
	 * 获取当前时间
	 */
	public static String GetDate() {
		SimpleDateFormat simpleDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return simpleDate.format(new java.util.Date()).replaceAll("[^0-9]", "-");
	}

	/**
	 * 初始化SDK库
	 */
	public static boolean Init() {
		bInit = netsdk.CLIENT_Init(disConnectCB, null);// 进程启动时，初始化一次
		if (!bInit) {
			System.out.println("Initialize SDK failed");
			return false;
		}
		// 配置日志
		REIDConfigDemo.enableLog();

		// 设置断线重连回调接口, 此操作为可选操作，但建议用户进行设置
		netsdk.CLIENT_SetAutoReconnect(haveReConnectCB, null);

		// 设置登录超时时间和尝试次数，可选
		// 登录请求响应超时时间设置为3S
		int waitTime = 3000;
		// 登录时尝试建立链接 1 次
		int tryTimes = 1;
		netsdk.CLIENT_SetConnectTime(waitTime, tryTimes);
		// 设置更多网络参数， NET_PARAM 的nWaittime ， nConnectTryNum 成员与 CLIENT_SetConnectTime
		// 接口设置的登录设备超时时间和尝试次数意义相同,可选
		NetSDKLib.NET_PARAM netParam = new NetSDKLib.NET_PARAM();
		// 登录时尝试建立链接的超时时间
		netParam.nConnectTime = 10000;
		// 设置子连接的超时时间
		netParam.nGetConnInfoTime = 3000;
		netsdk.CLIENT_SetNetworkParam(netParam);
		return true;
	}

	/**
	 * 打开 sdk log
	 */
	private static void enableLog() {
		NetSDKLib.LOG_SET_PRINT_INFO setLog = new NetSDKLib.LOG_SET_PRINT_INFO();
		File path = new File("sdklog/");
		if (!path.exists())
			path.mkdir();

		// 这里的log保存地址依据实际情况自己调整
		String logPath = path.getAbsoluteFile().getParent() + "\\sdklog\\" + "sdklog" + GetDate() + ".log";
		setLog.nPrintStrategy = 0;
		setLog.bSetFilePath = 1;
		System.arraycopy(logPath.getBytes(), 0, setLog.szLogFilePath, 0, logPath.getBytes().length);
		System.out.println(logPath);
		setLog.bSetPrintStrategy = 1;
		bLogOpen = netsdk.CLIENT_LogOpen(setLog);
		if (!bLogOpen)
			System.err.println("Failed to open NetSDK log");
	}

	/**
	 * 高安全登录
	 */
	public void loginWithHighLevel() {
		// 输入结构体参数
		NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY pstlnParam = new NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY() {
			{
				szIP = m_strIpAddr.getBytes();
				nPort = m_nPort;
				szUserName = m_strUser.getBytes();
				szPassword = m_strPassword.getBytes();
			}
		};
		// 输出结构体参数
		NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY pstOutParam = new NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY();

		// 写入sdk
		m_hLoginHandle = netsdk.CLIENT_LoginWithHighLevelSecurity(pstlnParam, pstOutParam);
		if (m_hLoginHandle.longValue() == 0) {
			System.err.printf("Login Device[%s] Port[%d]Failed. %s\n", m_strIpAddr, m_nPort,
					netsdk.CLIENT_GetLastError());
		} else {
			deviceInfo = pstOutParam.stuDeviceInfo; // 获取设备信息
			System.out.println("Login Success");
			System.out.println("Device Address：" + m_strIpAddr);
			System.out.println("设备包含：" + deviceInfo.byChanNum + "个通道");
		}
	}

	/**
	 * 退出
	 */
	public void logOut() {
		if (m_hLoginHandle.longValue() != 0) {
			netsdk.CLIENT_Logout(m_hLoginHandle);
			System.out.println("LogOut Success");
		}
	}

	/**
	 * 清理sdk环境并退出
	 */
	public static void cleanAndExit() {
		if (bLogOpen) {
			netsdk.CLIENT_LogClose(); // 关闭sdk日志打印
		}
		netsdk.CLIENT_Cleanup(); // 进程关闭时，调用一次
		System.exit(0);
	}

	/**
	 * 查找工装合规组信息
	 */
	public void findWorkSuitCompareGroup() {
		// 入参
		NET_IN_FIND_WORKSUIT_COMPARE_GROUP pstInParam = new NET_IN_FIND_WORKSUIT_COMPARE_GROUP();
		// 合规库组ID, 不填组ID表示查找全部组信息`
		// String szGroupID = "1";
		// System.arraycopy(szGroupID.getBytes(), 0,pstInParam.szGroupID,
		// 0,szGroupID.getBytes().length);
		pstInParam.write();

		// 出参
		NET_OUT_FIND_WORKSUIT_COMPARE_GROUP pstOutParam = new NET_OUT_FIND_WORKSUIT_COMPARE_GROUP();
		// 工装合规组最大个数, 由用户指定, 即pstWorkSuitGroupInfo 数组大小
		int nMaxGroupNum = 10; // 查询的最大值,如果设置的过大，会溢出，需要在eclipse里设置，增加堆内存
		NET_WORKSUIT_COMPARE_GROUP_INFO[] arr = new NET_WORKSUIT_COMPARE_GROUP_INFO[nMaxGroupNum];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = new NET_WORKSUIT_COMPARE_GROUP_INFO();
		}
		// 工装合规组信息指针初始化
		pstOutParam.nMaxGroupNum = arr[0].size() * nMaxGroupNum; // 工装合规组信息数组大小
		pstOutParam.pstWorkSuitGroupInfo = new Memory(arr[0].size() * nMaxGroupNum); // 初始化Pointer指针
		pstOutParam.pstWorkSuitGroupInfo.clear(arr[0].size() * nMaxGroupNum);

		ToolKits.SetStructArrToPointerData(arr, pstOutParam.pstWorkSuitGroupInfo); // 将数组内存拷贝给指针
		pstOutParam.write();

		// 调用接口
		boolean flg = netsdk.CLIENT_FindWorkSuitCompareGroup(m_hLoginHandle, pstInParam.getPointer(),
				pstOutParam.getPointer(), 3000);
		if (flg) {
			pstOutParam.read();
			System.out.println("实际返回的工装合规组个数:" + pstOutParam.nRetGroupNum);
			ToolKits.GetPointerDataToStructArr(pstOutParam.pstWorkSuitGroupInfo, arr); // 将指针的内容放入数组
			for (int i = 0; i < pstOutParam.nRetGroupNum; i++) {
				System.out.println("---------第" + (i + 1) + "个工装合规组信息-------------");
				try {
					System.out.println("合规组ID:" + new String(arr[i].szGroupID, encode));
					System.out.println("合规组名称:" + new String(arr[i].szGroupName, encode));
					System.out.println("合规组备注信息:" + new String(arr[i].szGroupDetail, encode));
					System.out.println("工装优选方案:" + EM_WORKSUIT_GROUP_POLICY.getNoteByValue(arr[i].emWorkSuitPolicy));
					System.out.println("相似度阈值:" + arr[i].nSimilarity);
					System.out.println("合规组类型:" + EM_WORKSUIT_GROUP_TYPE.getNoteByValue(arr[i].emGroupType));
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
			}
		} else {
			System.err.println("查找工装合规组信息失败:" + ToolKits.getErrorCode());
		}

	}

	/**
	 * 创建工装合规组
	 * 
	 * @throws UnsupportedEncodingException
	 */
	public void createWorkSuitCompareGroup() throws UnsupportedEncodingException {
		// 入参
		NET_IN_CREATE_WORKSUIT_COMPARE_GROUP pstInParam = new NET_IN_CREATE_WORKSUIT_COMPARE_GROUP();
		// 工装合规组信息
		// 合规组名称
		String szGroupName = "快递"; // 注意字符编码导致的乱码问题
		System.arraycopy(szGroupName.getBytes(encode), 0, pstInParam.stuWorkSuitGroupInfo.szGroupName, 0,
				szGroupName.getBytes(encode).length);
		// 合规组备注信息
		String szGroupDetail = "小区业主";
		System.arraycopy(szGroupDetail.getBytes(encode), 0, pstInParam.stuWorkSuitGroupInfo.szGroupDetail, 0,
				szGroupDetail.getBytes(encode).length);
		// 工装优选方案 {@link com.netsdk.lib.enumeration.EM_WORKSUIT_GROUP_POLICY}
		pstInParam.stuWorkSuitGroupInfo.emWorkSuitPolicy = EM_WORKSUIT_GROUP_POLICY.EM_WORKSUIT_GROUP_POLICY_WHOLE_BODY
				.getValue();
		// 相似度阈值，范围 [1, 100]，默认67
		pstInParam.stuWorkSuitGroupInfo.nSimilarity = 67;
		// 合规组类型 {@link com.netsdk.lib.enumeration.EM_WORKSUIT_GROUP_TYPE}
		pstInParam.stuWorkSuitGroupInfo.emGroupType = EM_WORKSUIT_GROUP_TYPE.EM_WORKSUIT_GROUP_TYPE_BLOCK.getValue();
		pstInParam.write();

		// 出参
		NET_OUT_CREATE_WORKSUIT_COMPARE_GROUP pstOutParam = new NET_OUT_CREATE_WORKSUIT_COMPARE_GROUP();
		pstOutParam.write();

		// 调用接口
		boolean flg = netsdk.CLIENT_CreateWorkSuitCompareGroup(m_hLoginHandle, pstInParam.getPointer(),
				pstOutParam.getPointer(), 3000);
		if (flg) {
			pstOutParam.read();
			System.out.println("合规库组ID:" + new String(pstOutParam.szGroupID).trim());
		} else {
			System.err.println("创建工装合规组失败:" + ToolKits.getErrorCode());
		}

	}

	/**
	 * 删除工装合规组
	 */
	public void deleteWorkSuitCompareGroup() {
		// 入参
		NET_IN_DELETE_WORKSUIT_COMPARE_GROUP pstInParam = new NET_IN_DELETE_WORKSUIT_COMPARE_GROUP();
		// 合规库组ID
		String szGroupID = "1";
		System.arraycopy(szGroupID.getBytes(), 0, pstInParam.szGroupID, 0, szGroupID.getBytes().length);
		pstInParam.write();

		// 出参
		NET_OUT_DELETE_WORKSUIT_COMPARE_GROUP pstOutParam = new NET_OUT_DELETE_WORKSUIT_COMPARE_GROUP();
		pstOutParam.write();

		// 调用接口
		boolean flg = netsdk.CLIENT_DeleteWorkSuitCompareGroup(m_hLoginHandle, pstInParam.getPointer(),
				pstOutParam.getPointer(), 3000);
		if (flg) {
			pstOutParam.read();
			System.out.println("删除工装合规组成功");
		} else {
			System.err.println("删除工装合规组失败:" + ToolKits.getErrorCode());
		}
	}

	/**
	 * 批量添加工装合规样本-下发图片路径
	 * 
	 * @throws UnsupportedEncodingException
	 */
	public void multiAppendToWorkSuitCompareGroup() throws UnsupportedEncodingException {
		// 入参
		NET_IN_MULTI_APPEND_TO_WORKSUIT_GROUP pstInParam = new NET_IN_MULTI_APPEND_TO_WORKSUIT_GROUP();
		int nWorkSuitSampleNum = 2;
		pstInParam.nWorkSuitSampleNum = nWorkSuitSampleNum;// 工装合规样本个数, 由用户指定大小
		NET_WORKSUIT_SAMPLE_INFO[] arr = new NET_WORKSUIT_SAMPLE_INFO[nWorkSuitSampleNum];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = new NET_WORKSUIT_SAMPLE_INFO();// 初始化数组
		}
		/***
		 * 1.每次下发，图片只能下发同一个合规库组ID、合规库组名称(每个token下发时只能对应一个合规库组ID)；
		 * 2.每次下发token值，随机生成且值不能一样,不然会导致上传异常
		 */
		// 样本一
		// 全景图唯一标识符，由平台生成
		String szSourceUID1 = "A1";
		System.arraycopy(szSourceUID1.getBytes(encode), 0, arr[0].szSourceUID, 0, szSourceUID1.getBytes(encode).length);
		// 合规库组ID,创建工装合规组的出参
		String szGroupID1 = "1";
		System.arraycopy(szGroupID1.getBytes(encode), 0, arr[0].szGroupID, 0, szGroupID1.getBytes(encode).length);
		// 合规库组名称,创建工装合规组的入参
		String szGroupName1 = "快递";
		System.arraycopy(szGroupName1.getBytes(encode), 0, arr[0].szGroupName, 0, szGroupName1.getBytes(encode).length);
		// 全景图图片名称
		String szSourceFileName1 = "图片1";
		System.arraycopy(szSourceFileName1.getBytes(encode), 0, arr[0].szSourceFileName, 0,
				szSourceFileName1.getBytes(encode).length);
		// 文件路径, 支持HTTP URL表示; 支持FTP URL表示; 支持服务器本地路径,填写图片路径后无需填写图片信息中的offset与length字段
		String szFilePath1 = "http://10.33.12.11/httpImg/work.jpg";
		System.arraycopy(szFilePath1.getBytes(encode), 0, arr[0].szFilePath, 0, szFilePath1.getBytes(encode).length);
		// 样本二
		// 全景图唯一标识符，由平台生成
		String szSourceUID2 = "A2";
		System.arraycopy(szSourceUID2.getBytes(encode), 0, arr[1].szSourceUID, 0, szSourceUID2.getBytes(encode).length);
		// 合规库组ID,创建工装合规组的出参
		String szGroupID2 = "1";
		System.arraycopy(szGroupID2.getBytes(encode), 0, arr[1].szGroupID, 0, szGroupID2.getBytes(encode).length);
		// 合规库组名称,创建工装合规组的入参
		String szGroupName2 = "快递";
		System.arraycopy(szGroupName2.getBytes(encode), 0, arr[1].szGroupName, 0, szGroupName2.getBytes(encode).length);
		// 全景图图片名称
		String szSourceFileName2 = "图片2";
		System.arraycopy(szSourceFileName2.getBytes(encode), 0, arr[1].szSourceFileName, 0,
				szSourceFileName2.getBytes(encode).length);
		// 文件路径, 支持HTTP URL表示; 支持FTP URL表示; 支持服务器本地路径,填写图片路径后无需填写图片信息中的offset与length字段
		String szFilePath2 = "http://10.33.12.11/httpImg/work.jpg";
		System.arraycopy(szFilePath2.getBytes(encode), 0, arr[1].szFilePath, 0, szFilePath2.getBytes(encode).length);

		// 工装合规样本信息赋值到指针
		pstInParam.pstWorkSuitSampleInfo = new Memory(arr[0].size() * nWorkSuitSampleNum); // 初始化Pointer指针
		pstInParam.pstWorkSuitSampleInfo.clear(arr[0].size() * nWorkSuitSampleNum);
		ToolKits.SetStructArrToPointerData(arr, pstInParam.pstWorkSuitSampleInfo); // 将数组内存拷贝给指针

		// 工装合规导入标识信息
		// 该次图片导入的token值，随机生成且不能一样,不然会导致上传异常，每个token下发时只能对应一个合规库组ID,建议使用数据库需要或者每次操作后加一处理
		pstInParam.stuAppendIdentiInfo.nToken = randomNum();
		pstInParam.write();

		// 出参
		NET_OUT_MULTI_APPEND_TO_WORKSUIT_GROUP pstOutParam = new NET_OUT_MULTI_APPEND_TO_WORKSUIT_GROUP();
		pstOutParam.write();

		// 调用接口
		boolean flg = netsdk.CLIENT_MultiAppendToWorkSuitCompareGroup(m_hLoginHandle, pstInParam.getPointer(),
				pstOutParam.getPointer(), 3000);
		if (flg) {
			pstOutParam.read();
			System.out.println("工装合规导入返回的结果码信息--------");
			System.out.println("工装合规导入返回的结果码个数:" + pstOutParam.nErrCodeNum);
			int[] emErrCode = pstOutParam.emErrCode;
			for (int i = 0; i < pstOutParam.nErrCodeNum; i++) {
				System.out.println("第" + (i + 1) + "个错误码信息：" + EM_WORKSUIT_APPEND_ERRCODE.getNoteByValue(emErrCode[i]));
			}
		} else {
			System.err.println("批量添加工装合规样本失败:" + ToolKits.getErrorCode());
		}
	}

	public int randomNum() {
		long randomNum = System.currentTimeMillis();
		int randomNum1 = (int) randomNum;
		System.out.println("token:" + randomNum1);
		return randomNum1;
	}

	/**
	 * 通过全景图唯一标识符删除工装合规样本
	 * 
	 * @throws UnsupportedEncodingException
	 */
	public void deleteWorkSuitBySourceUID() throws UnsupportedEncodingException {
		// 入参
		NET_IN_DELETE_WORKSUIT_BY_SOURCEUID pstInParam = new NET_IN_DELETE_WORKSUIT_BY_SOURCEUID();
		// 全景图唯一标识符个数
		pstInParam.nSourceUIDNum = 2;
		String szSourceUID1 = "A1";
		String szSourceUID2 = "A2";
		System.arraycopy(szSourceUID1.getBytes(encode), 0, pstInParam.szSourceUID[0].arr, 0,
				szSourceUID1.getBytes(encode).length);
		System.arraycopy(szSourceUID2.getBytes(encode), 0, pstInParam.szSourceUID[1].arr, 0,
				szSourceUID2.getBytes(encode).length);
		// 工装合规组ID
		String szGroupID = "1";
		System.arraycopy(szGroupID.getBytes(encode), 0, pstInParam.szGroupID, 0, szGroupID.getBytes(encode).length);
		pstInParam.write();

		// 出参
		NET_OUT_DELETE_WORKSUIT_BY_SOURCEUID pstOutParam = new NET_OUT_DELETE_WORKSUIT_BY_SOURCEUID();
		pstOutParam.write();

		// 调用接口
		boolean flg = netsdk.CLIENT_DeleteWorkSuitBySourceUID(m_hLoginHandle, pstInParam.getPointer(),
				pstOutParam.getPointer(), 3000);
		if (flg) {
			pstOutParam.read();
			System.out.println("通过全景图唯一标识符删除工装合规样返回的结果码信息--------");
			System.out.println("通过全景图唯一标识符删除工装合规样返回的结果码个数:" + pstOutParam.nErrCodeNum);
			int[] emErrCode = pstOutParam.emErrCode;
			for (int i = 0; i < pstOutParam.nErrCodeNum; i++) {
				System.out.println(
						"第" + (i + 1) + "个错误码信息：" + EM_DELETE_BY_SOURCEUID_ERRCODE.getNoteByValue(emErrCode[i]));
			}
		} else {
			System.err.println("通过全景图唯一标识符删除工装合规样本失败:" + ToolKits.getErrorCode());
		}
	}

	/**
	 * 订阅报警信息
	 * 
	 */
	public void startListen() {
		// 设置报警回调函数
		netsdk.CLIENT_SetDVRMessCallBack(fAlarmAccessDataCB.getInstance(), null);

		// 订阅报警
		boolean bRet = netsdk.CLIENT_StartListenEx(m_hLoginHandle);
		if (!bRet) {
			System.err.println("订阅报警失败! LastError = 0x%x\n" + ToolKits.getErrorCode());
		} else {
			System.out.println("订阅报警成功.");
		}
	}

	/**
	 * 报警事件回调
	 */
	private static class fAlarmAccessDataCB implements NetSDKLib.fMessCallBack {
		private static fAlarmAccessDataCB instance = new fAlarmAccessDataCB();

		private fAlarmAccessDataCB() {
		}

		public static fAlarmAccessDataCB getInstance() {
			return instance;
		}

		@Override
		public boolean invoke(int lCommand, LLong lLoginID, Pointer pStuEvent, int dwBufLen, String strDeviceIP,
				NativeLong nDevicePort, Pointer dwUser) {
			switch (lCommand) {
			case NetSDKLib.NET_ALARM_WORKSUIT_FEATURE_ABSTRACT: {// 工装特征向量建模结果上报事件(对应结构体ALARM_WORKSUIT_FEATURE_ABSTRACT_INFO)
				System.out.println("-----工装特征向量建模结果上报事件-----");
				ALARM_WORKSUIT_FEATURE_ABSTRACT_INFO msg = new ALARM_WORKSUIT_FEATURE_ABSTRACT_INFO();
				ToolKits.GetPointerData(pStuEvent, msg);
				System.out.println("事件动作:" + msg.nAction);
				System.out.println("事件发生的时间:" + msg.stuTime.toStringTime());
				System.out.println("工装建模结果个数:" + msg.nAbstractInfoNum);
				NET_WORKSUIT_ABSTRACT_INFO[] stuAbstractInfo = msg.stuAbstractInfo;
				for (int i = 0; i < msg.nAbstractInfoNum; i++) {
					System.out.println("----第" + (i + 1) + "个工装建模结果信息----");
					System.out.println("工装合规组ID:" + new String(stuAbstractInfo[i].szGroupID));
					System.out.println("工装建模进度,百分比:" + stuAbstractInfo[i].nProcess);
					// System.out.println("该次工装建模的token值:"+stuAbstractInfo[i].nToken);
					System.out.println("工装图片建模成功的个数:" + stuAbstractInfo[i].nSucceedCnt);
					System.out.println("工装图片建模失败的个数:" + stuAbstractInfo[i].nFailedCnt);
				}

				break;
			}
			case NetSDKLib.NET_ALARM_REID_CLUSTER_STATE: {// REID项目上报设备聚档状态事件(对应结构体 NET_ALARM_REID_CLUSTER_STATE_INFO)
				System.out.println("-----REID项目上报设备聚档状态事件-----");
				NET_ALARM_REID_CLUSTER_STATE_INFO msg = new NET_ALARM_REID_CLUSTER_STATE_INFO();
				ToolKits.GetPointerData(pStuEvent, msg);
				System.out.println("事件动作:" + msg.nAction);
				System.out.println("事件发生的时间:" + msg.stuUTC.toStringTime());
				System.out.println("当前聚档状态 :" + msg.nClusterState);// 当前聚档状态 1.已完成. 2.进行中 3.聚档失败
				System.out.println("上次聚档触发时间 :" + msg.nClusterDate);// 上次聚档触发时间，按照年月日记录，作为聚档的唯一key，单设备每日只有一条聚档记录.如20220530则表示在20220530当日已经聚档
				System.out.println("上次聚档类型 :" + msg.nClusterType);// 上次聚档类型: 1.自动每日聚档 2.平台下发手动聚档 3.手动测试触发 4.聚档主动失败重试
				System.out.println("记录更新时间:" + msg.stuUTC.toStringTime());// 记录更新时间，已设备本地时间为准
				break;
			}
			default:
				System.out.printf("Get Other Event 0x%x\n", lCommand);
				break;
			}
			return true;
		}
	}

	/**
	 * 取消订阅报警信息
	 * 
	 * @return
	 */
	public void stopListen() {
		// 停止订阅报警
		boolean bRet = netsdk.CLIENT_StopListen(m_hLoginHandle);
		if (bRet) {
			System.out.println("取消订阅报警信息.");
		}
	}

	/**
	 * 异步查询聚档完整数据
	 */
	public void findDetailNumberStatCluster() {
		// 创建查询 下发条件
		NET_IN_START_FIND_DETAIL_CLUSTER pstInParam = new NET_IN_START_FIND_DETAIL_CLUSTER();
		pstInParam.stuCondition.nClusterDate = 20220925;
		pstInParam.write();
		NET_OUT_START_FIND_DETAIL_CLUSTER pstOutParam = new NET_OUT_START_FIND_DETAIL_CLUSTER();
		pstOutParam.write();
		boolean flg = netsdk.CLIENT_StartFindDetailNumberStatCluster(m_hLoginHandle, pstInParam.getPointer(),
				pstOutParam.getPointer(), 3000);
		if (flg) {
			pstOutParam.read();
			System.out.println("取到的查询令牌:" + pstOutParam.nToken);
			System.out.println("记录总数:" + pstOutParam.nCount);
			System.out.println("查询该天的营业开始时间:" + pstOutParam.stuClusterBeginTime.toStringTime());
			System.out.println("查询该天的营业结束时间:" + pstOutParam.stuClusterEndTime.toStringTime());
		} else {
			System.err.println("创建查询 下发条件失败:" + ToolKits.getErrorCode());
			return;
		}

		// 分布查询结果
		NET_IN_DO_FIND_DETAIL_CLUSTER pstInParam1 = new NET_IN_DO_FIND_DETAIL_CLUSTER();
		pstInParam1.nToken = pstOutParam.nToken; // start接口返回的token, token保存的数量有限, 未stop的会在连接销毁时或队列满时被释放,
													// 再用同一token访问会导致失败
		pstInParam1.nCount = pstOutParam.nCount;// 查询的记录数，单次不超过1024
		pstInParam1.nStartIndex = 0;// 查询记录的开始偏移
		pstInParam1.write();
		NET_OUT_DO_FIND_DETAIL_CLUSTER pstOutParam1 = new NET_OUT_DO_FIND_DETAIL_CLUSTER();
		pstOutParam1.write();
		boolean flg1 = netsdk.CLIENT_DoFindDetailNumberStatCluster(m_hLoginHandle, pstInParam1.getPointer(),
				pstOutParam1.getPointer(), 3000);
		if (flg1) {
			pstOutParam1.read();
			System.out.println("查询记录条数:" + pstOutParam1.nFound);
			System.out.println("返回记录实际条数:" + pstOutParam1.nFindInfoRealNum);
			System.out.println("打印其中的一条记录数据-----");
			if (pstOutParam1.nFindInfoRealNum > 1) {
				System.out.println("设备端数据库ID：" + pstOutParam1.stuClusterFindInfo[1].nRcNo);
				System.out.println("人员记录的唯一Key：" + new String(pstOutParam1.stuClusterFindInfo[1].szTrackID));
				System.out.println("聚档结果ID：" + pstOutParam1.stuClusterFindInfo[1].nProfileID);
				System.out.println("抓拍通道号：" + pstOutParam1.stuClusterFindInfo[1].nChannel);
				System.out.println("年龄：" + pstOutParam1.stuClusterFindInfo[1].nAge);
				System.out.println("性别：" + pstOutParam1.stuClusterFindInfo[1].nGender);
				System.out.println("人员的经过属性：" + pstOutParam1.stuClusterFindInfo[1].nHumanState);
			}

		} else {
			System.err.println("分布查询结果失败:" + ToolKits.getErrorCode());
			return;
		}

		// 销毁查询对象
		NET_IN_STOP_FIND_DETAIL_CLUSTER_INFO pstInParam2 = new NET_IN_STOP_FIND_DETAIL_CLUSTER_INFO();
		pstInParam2.nToken = pstOutParam.nToken; // start接口返回的token, token仅会保存
		pstInParam2.write();
		NET_OUT_STOP_FIND_DETAIL_CLUSTER_INFO pstOutParam2 = new NET_OUT_STOP_FIND_DETAIL_CLUSTER_INFO();
		pstOutParam2.write();
		boolean flg2 = netsdk.CLIENT_StopFindDetailNumberStatCluster(m_hLoginHandle, pstInParam2.getPointer(),
				pstOutParam2.getPointer(), 3000);
		if (flg2) {
			pstOutParam2.read();
			System.out.println("销毁查询对象成功");
		} else {
			System.err.println("销毁查询对象失败:" + ToolKits.getErrorCode());
		}
	}

	/**
	 * 平台主动获取设备聚档状态
	 */
	public void getClusterState() {
		NET_IN_GET_CLUSTER_STATE_INFO pstInParam = new NET_IN_GET_CLUSTER_STATE_INFO();
		pstInParam.nClusterDate = 20220925; // 非必选, 当包含该字段时查对应日期的聚档状态, 否则返回最近一次的状态
		pstInParam.write();
		NET_OUT_GET_CLUSTER_STATE_INFO pstOutParam = new NET_OUT_GET_CLUSTER_STATE_INFO();
		pstOutParam.write();
		boolean flg = netsdk.CLIENT_GetClusterState(m_hLoginHandle, pstInParam.getPointer(), pstOutParam.getPointer(),
				3000);
		if (flg) {
			pstOutParam.read();
			System.out.println("当前聚档状态:" + pstOutParam.nClusterState);// 当前聚档状态 1.已完成. 2.进行中 3.聚档失败
			System.out.println("记录更新时间:" + pstOutParam.stuDealTime.toStringTime());// 记录更新时间，已设备本地时间为准
			System.out.println("聚档触发时间:" + pstOutParam.nClusterState);// 聚档触发时间, 按照年月日记录,
																		// 作为聚档的唯一key,单设备每日只有一条聚档记录.如20220530则表示在20220530当日已经聚档
			System.out.println("上次聚档类型:" + pstOutParam.nClusterType);// 上次聚档类型: 1.自动每日聚档 2.平台下发手动聚档 3.手动测试触发 4.聚档主动失败重试
		} else {
			System.err.println("平台主动获取设备聚档状态失败:" + ToolKits.getErrorCode());
			return;
		}

	}

	int channel = 14;

	/**
	 * 订阅统计通道数据
	 * 
	 * @throws UnsupportedEncodingException
	 */
	public void attachVideoStatStream() throws UnsupportedEncodingException {
		NET_IN_ATTACH_VIDEOSTAT_STREAM input = new NET_IN_ATTACH_VIDEOSTAT_STREAM();
		// 视频通道号
		input.nVideoChannel = channel;
		// 粒度模式,EM_STAT_GRANULARITY
		input.emGranularity = 1;
		// "NumberStat"：人数统计，统计进入离开数量 ; "ManNumDetection"：区域内人数统计，统计区域内人员数量及滞留时间
		String ruleType = "ManNumDetection";
		System.arraycopy(ruleType.getBytes(encode), 0, input.szRuleType, 0, ruleType.getBytes(encode).length);
		// 开始时间，表示从什么时间开始统计上报，开始时间，需按周期取整。如推送周期是10分钟一次，那么开始时间，只能为，每个小时的
		// 第0分，10分、20分、30分、40、分、50分。
		String sartTime = "2022-12-13 08:00:00";
		System.arraycopy(sartTime.getBytes(encode), 0, input.szStartTime, 0, sartTime.getBytes(encode).length);
		// 与Granularity结合使用，如emGranularity为 EM_GRANULARITY_MINUTE， Cycle
		// 值为5，表示以5分钟统计周期上报数据
		input.nCycle = 1;
		// 统计通道数据回调
		input.cbVideoStatStream = VideoStatStreamCallBack.getInstance();
		if ("ManNumDetection".equals(ruleType)) {
			// 滞留情况数组大小，如填0则不下发该字段
			input.nSendMinStayTimeValidNum = 2;
			// 上报时间段内区域人员滞留情况, 表示统计的间隔结果中返回滞留时间大于3秒，大于5秒，大于10秒的记录,单位秒, 不下发时回调仅返回总人数和总滞留时长
			input.nMinStayTime[0] = 1;
			input.nMinStayTime[1] = 2;
		}

		input.write();

		NET_OUT_ATTACH_VIDEOSTAT_STREAM outPut = new NET_OUT_ATTACH_VIDEOSTAT_STREAM();
		outPut.write();

		attachHandle = netsdk.CLIENT_AttachVideoStatStream(m_hLoginHandle, input.getPointer(), outPut.getPointer(),
				3000);
		if (attachHandle.longValue() == 0) {
			System.out.printf("Chn[%d] CLIENT_AttachVideoStatStream Failed!LastError = %s\n", channel,
					ToolKits.getErrorCode());
		} else {
			System.out.printf("Chn[%d] CLIENT_AttachVideoStatStream Success\n", channel);
		}

	}

	/**
	 * 取消订阅统计通道数据
	 */
	public void detachVideoStatStream() {
		boolean isSuccess = netsdk.CLIENT_DetachVideoStatStream(attachHandle);
		if (isSuccess) {
			System.out.println(" CLIENT_DetachVideoStatStream Success");
		} else {
			System.out.printf("Chn[%d] CLIENT_DetachVideoStatStream Failed!LastError = %s\n", channel,
					ToolKits.getErrorCode());
		}
	}

	/**
	 * 统计通道数据信息
	 */
	private static class VideoStatStreamCallBack implements NetSDKLib.fVideoStatStreamCallBack {
		private static VideoStatStreamCallBack instance;

		public static VideoStatStreamCallBack getInstance() {
			if (instance == null) {
				synchronized (VideoStatStreamCallBack.class) {
					if (instance == null) {
						instance = new VideoStatStreamCallBack();
					}
				}
			}
			return instance;
		}

		@Override
		public void invoke(LLong lAttachHandle, Pointer pBuf, int nBufLen, Pointer dwUser) {
			System.out.println("---------统计通道数据信息----------");
			NET_CB_VIDEOSTAT_STREAM msg = new NET_CB_VIDEOSTAT_STREAM();
			ToolKits.GetPointerData(pBuf, msg);
			System.out.println("统计通道号: " + msg.nChannel);
			try {
				System.out.println("规则名称: " + new String(msg.szRuleNanme, encode));
				System.out.println("当前这一统计周期的开始时间: " + new String(msg.szStartTime, encode));
				System.out.println("规则类型: " + new String(msg.szRuleType, encode));
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			System.out.println("粒度: " + msg.emStatGranularity);
			System.out.println("统计周期: " + msg.nCycle);
			System.out.println("区域ID，没有该字段或者为0，为单区域: " + msg.nAreaID);
			System.out.println("本周期内进入人数小计: " + msg.nEnteredSubtotal);
			System.out.println("本周期内离开人数小计: " + msg.nExitedSubtotal);
			System.out.println("本周期内经过人数小计: " + msg.nPassedSubtotal);
			// 本周期区域内滞留人员统计信息, bIsSendMinStayTime为TRUE时有效 ruleType为“ManNumDetection”时，上报数据
			System.out.println(" 本周期上报的区域内总人数: " + msg.stuInsideSubtotal.nTotal);
			System.out.println(" 当前周期，所有人的总滞留时长: " + msg.stuInsideSubtotal.nTotalStayTime);
			System.out.println(" 人次数据有效个数: " + msg.stuInsideSubtotal.nManStaySummaryValidNum);
			if (msg.stuInsideSubtotal.nManStaySummaryValidNum > 0) {
				System.out.println("人次数据-----------------------");
				// 人次数据,例此处下发的规则为滞留时间大于3,5,10,300秒的规则.此处结果就是滞留时间大于3秒共10人次，大于5秒的6人次，10秒的3人次，300秒的0人次
				int[] nManStaySummary = msg.stuInsideSubtotal.nManStaySummary;
				for (int i = 0; i < msg.stuInsideSubtotal.nManStaySummaryValidNum; i++) {
					System.out.println(" nManStaySummary[" + i + "]: " + nManStaySummary[i]);
				}
			}

			System.out.println(" 总滞留时长有效个数: " + msg.stuInsideSubtotal.nTotalStayTimeSummaryValidNum);
			if (msg.stuInsideSubtotal.nTotalStayTimeSummaryValidNum > 0) {
				System.out.println("总滞留时长，单位秒-----------------------");
				// 总滞留时长，单位秒,
				// 例此处下发的规则为滞留时间大于3,5,10,300秒的规则,此处结果就是滞留时间大于3秒的人员的总滞留时长50秒，大于5秒的48秒，10秒的30秒，300秒的0秒
				int[] nTotalStayTimeSummary = msg.stuInsideSubtotal.nTotalStayTimeSummary;
				for (int i = 0; i < msg.stuInsideSubtotal.nTotalStayTimeSummaryValidNum; i++) {
					System.out.println(" nTotalStayTimeSummary[" + i + "]: " + nTotalStayTimeSummary[i]);
				}
			}

		}

	}

	/******************************** 测试控制台 ***************************************/

	// 配置登陆地址，端口，用户名，密码
	private String m_strIpAddr = "172.13.51.83";
	private int m_nPort = 37777;
	private String m_strUser = "admin";
	private String m_strPassword = "admin123";

	public static void main(String[] args) {
		REIDConfigDemo demo = new REIDConfigDemo();
		demo.InitTest();
		demo.RunTest();
		demo.EndTest();

	}

	/**
	 * 初始化测试
	 */
	public void InitTest() {
		REIDConfigDemo.Init();
		this.loginWithHighLevel();
	}

	/**
	 * 加载测试内容
	 */
	public void RunTest() {
		CaseMenu menu = new CaseMenu();
		menu.addItem(new CaseMenu.Item(this, "查找工装合规组信息", "findWorkSuitCompareGroup"));
		menu.addItem(new CaseMenu.Item(this, "创建工装合规组", "createWorkSuitCompareGroup"));
		menu.addItem(new CaseMenu.Item(this, "删除工装合规组", "deleteWorkSuitCompareGroup"));
		menu.addItem(new CaseMenu.Item(this, "批量添加工装合规样本", "multiAppendToWorkSuitCompareGroup"));
		menu.addItem(new CaseMenu.Item(this, "通过全景图唯一标识符删除工装合规样本", "deleteWorkSuitBySourceUID"));

		menu.addItem(new CaseMenu.Item(this, "订阅报警信息", "startListen"));
		menu.addItem(new CaseMenu.Item(this, "取消订阅报警信息", "stopListen"));

		menu.addItem(new CaseMenu.Item(this, "异步查询聚档完整数据", "findDetailNumberStatCluster"));
		menu.addItem(new CaseMenu.Item(this, "平台主动获取设备聚档状态", "getClusterState"));

		menu.addItem(new CaseMenu.Item(this, "订阅统计通道数据", "attachVideoStatStream"));
		menu.addItem(new CaseMenu.Item(this, "取消订阅统计通道数据", "detachVideoStatStream"));
		menu.run();
	}

	/**
	 * 结束测试
	 */
	public void EndTest() {
		System.out.println("End Test");
		this.logOut(); // 登出设备
		System.out.println("See You...");
		REIDConfigDemo.cleanAndExit(); // 清理资源并退出
	}
	/******************************** 结束 ***************************************/
}
